package com.portal.common.utils;

import com.google.common.util.concurrent.RateLimiter;
import lombok.extern.slf4j.Slf4j;

import javax.validation.constraints.NotNull;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Supplier;

/**
 * 包装一下
 * 1. 用 int 保存实际限流值，确保判断是否要修改限流的时候可以正确判断，避免浮点判断的问题。
 * 2. 可以根据限流值随时修改
 */
@SuppressWarnings("UnstableApiUsage")
@Slf4j
public final class MutableGuavaRateLimiter {
    /**
     * 100秒1个QPS，确保能走通，又不会卡死
     */
    private static final RateLimiter DEFAULT_STATIC_RATE_LIMITER = RateLimiter.create(0.01);

    private final Supplier<Integer> configMaxRateLimitSupplier;
    private volatile int currentMaxRateLimit;
    private volatile RateLimiter rateLimiter;
    private final String name;

    /**
     * @param configMaxRateLimitSupplier 限流数量的提供者
     * @param name                       for log
     */
    public MutableGuavaRateLimiter(Supplier<Integer> configMaxRateLimitSupplier, String name) {
        Objects.requireNonNull(name);
        Objects.requireNonNull(configMaxRateLimitSupplier);
        this.name = name;
        this.configMaxRateLimitSupplier = configMaxRateLimitSupplier;
        int configMaxRateLimit = Optional.ofNullable(configMaxRateLimitSupplier.get()).orElse(0);
        this.currentMaxRateLimit = configMaxRateLimit;
        if (configMaxRateLimit < 1) {
            rateLimiter = RateLimiter.create(0.01);
        } else {
            rateLimiter = RateLimiter.create(configMaxRateLimit);
        }
    }

    /**
     * 获取限流对象
     *
     * @return {@link #rateLimiter}
     */
    @NotNull
    public RateLimiter get() {
        renewFromConfigIfNecessary();
        return rateLimiter;
    }

    /**
     * 如果提供者的限流数据修改了，则重新生成一个限流对象
     */
    @NotNull
    private void renewFromConfigIfNecessary() {
        int configMaxRateLimit = Optional.ofNullable(this.configMaxRateLimitSupplier.get()).orElse(1);
        if (configMaxRateLimit < 1) {
            log.info("RateLimitChange DEFAULT_STATIC_RATE_LIMITER, {}, {} --> {}", this.name, this.currentMaxRateLimit, "default 0.01");
            rateLimiter = DEFAULT_STATIC_RATE_LIMITER;
            this.currentMaxRateLimit = configMaxRateLimit;
        }
        if (this.currentMaxRateLimit != configMaxRateLimit) {
            synchronized (this) {
                if (this.currentMaxRateLimit != configMaxRateLimit) {
                    log.info("RateLimitChange, {}, {} --> {}", this.name, this.currentMaxRateLimit, configMaxRateLimit);
                    rateLimiter = RateLimiter.create(configMaxRateLimit);
                    this.currentMaxRateLimit = configMaxRateLimit;
                }
            }
        }
    }
}
